package com.practice.concurrent;

import java.util.concurrent.TimeUnit;

/**
 * Future接口定义
 * @param <T>
 */
interface Future<T>{
    T get() throws InterruptedException;
    boolean isDone();
}

class FutureImpl<T> implements Future<T>{
    private T result;
    private boolean isDone = false;
    private Throwable exception;

    /**
     * 结果设置
     * @param result
     */
    public synchronized void complete(T result){
        this.result = result;
        this.isDone = true;
        notifyAll();
    }

    /**
     * 异常设置
     * @param ex
     */
    public synchronized void completeExceptionally(Throwable ex){
        this.exception = ex;
        this.isDone = true;
        notifyAll();
    }

    @Override
    public synchronized T get() throws InterruptedException {
        //使用while防止虚假唤醒
        while (!isDone){
            wait();
        }
        if(exception != null){
            throw new RuntimeException(exception);
        }
        return result;
    }

    @Override
    public boolean isDone() {
        return isDone;
    }
}

/**
 * 任务接口，类似Callable
 * @param <T>
 */
interface Task<T>{
    T execute() throws Exception;
}

/**
 * 服务类，负责提交任务并返回Future对象
 */
class FutureService{
    public <T> Future<T> submit(Task<T> task){
        FutureImpl<T> future = new FutureImpl<>();
        new Thread(()->{
            try {
                T result = task.execute();
                future.complete(result);
            }catch (Exception e){
                future.completeExceptionally(e);
            }
        }).start();
        return future;
    }
}

/**
 * 手写Future模式示例
 * Future 类是异步思想的典型运用，主要用在一些需要执行耗时任务的场景，避免程序一直原地等待耗时任务执行完成，执行效率太低。
 * 具体来说是这样的：当我们执行某一耗时的任务时，可以将这个耗时任务交给一个子线程去异步执行，同时我们可以干点其他事情，不用傻傻等待耗时任务执行完成。
 * 等我们的事情干完后，我们再通过 Future 类获取到耗时任务的执行结果。这样一来，程序的执行效率就明显提高了。
 */
public class ManualRutureDemo {

    public static void main(String[] args) {
        FutureService service = new FutureService();

        Future<String> future = service.submit(()->{
            TimeUnit.SECONDS.sleep(3);
            return "Operation completed!";
        });

        System.out.println("Doing other tasks");

        try {
            String result = future.get();
            System.out.println("Get result " + result);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

